﻿using HDS.Common.Helper;
using HDS.Data;
using HDS.IService.Master;
using HDS.Model;
using HDS.Model.Entity.Log;
using HDS.Model.Entity.Master;
using HDS.Model.Entity.View;
using HDS.SupplierApi.Core.Entity;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace HDS.Service.Master
{
    public class OrderHotelService : IOrderHotelService
    {
        private MasterDbContext dbContext = new MasterDbContext();
        private LogDbContext logDbContext = new LogDbContext();

        public async Task<OrderHotelDistributor> GetDistributorOrder(string orderId)
        {
            return await dbContext.OrderHotelDistributor.GetSingleAsync(e => e.OrderId.Equals(orderId));
        }

        public async Task<OrderHotelDistributor> GetDistributorOrder(string distributor, string distributorOrderId)
        {
            return await dbContext.OrderHotelDistributor.GetSingleAsync(e => e.Distributor.Equals(distributor) && e.DistributorOrderId.Equals(distributorOrderId));
        }

        public async Task<DataPagingModel<VOrderHotel>> GetDistributorOrderList(int pageIndex, int pageSize, Expression<Func<VOrderHotel, bool>> whereExpression, string whereSql)
        {
            var sql = "SELECT";
            sql += " od.`order_id`,";
            sql += " od.`order_status`,";
            sql += " od.`distributor`,";
            sql += " od.`distributor_order_id`,";
            sql += " od.`distributor_order_status`,";
            sql += " od.`hotel_id`,";
            sql += " od.`hotel_name`,";
            sql += " od.`hotel_address`,";
            sql += " od.`hotel_country_name`,";
            sql += " od.`hotel_city_name`,";
            sql += " od.`hotel_confirm_no`,";
            sql += " od.`room_id`,";
            sql += " od.`room_name`,";
            sql += " od.`rateplan_id`,";
            sql += " od.`room_count`,";
            sql += " od.`checkin`,";
            sql += " od.`checkout`,";
            sql += " od.`guest_room_json`,";
            sql += " od.`contact_name`,";
            sql += " od.`contact_phone`,";
            sql += " od.`contact_email`,";
            sql += " od.`meal_type`,";
            sql += " od.`meal_count`,";
            sql += " od.`free_cancel_time`,";
            sql += " od.`guest_note`,";
            sql += " od.`currency`,";
            sql += " od.`price`,";
            sql += " od.`created_time`,";
            sql += " od.`updated_time`,";
            sql += " os.`supplier`,";
            sql += " os.`supplier_order_id`,";
            sql += " os.`supplier_order_status`,";
            sql += " os.`supplier_error_code`,";
            sql += " os.`supplier_hotel_id`,";
            sql += " os.`supplier_room_id`,"; 
            sql += " os.`supplier_room_name`,";
            sql += " os.`supplier_meal_type`,";
            sql += " os.`supplier_meal_desc`,";
            sql += " os.`supplier_meal_count`,";
            sql += " os.`supplier_rateplan_id`,";
            sql += " os.`supplier_currency`,";
            sql += " os.`supplier_price`,";
            sql += " os.`supplier_free_cancel_time`,";
            sql += " os.`supplier_extras_json`,";
            sql += " os.`supplier_remark`";
            sql += " FROM `order_hotel_distributor` od";
            sql += " LEFT JOIN `order_hotel_supplier` os ON od.`order_id` = os.`order_id`";

            RefAsync<int> total = 0;
            var rows = await dbContext.Db.SqlQueryable<VOrderHotel>(sql)
                .WhereIF(whereExpression != null, whereExpression)
                .WhereIF(!string.IsNullOrEmpty(whereSql), whereSql)
                .OrderBy(e => e.CreatedTime, OrderByType.Desc)
                .ToPageListAsync(pageIndex, pageSize, total);

            return new DataPagingModel<VOrderHotel>
            {
                PageIndex = pageIndex,
                PageSize = pageSize,
                PageTotal = (total.Value + pageSize - 1) / pageSize,
                Total = total.Value,
                Rows = rows
            };
        }

        public async Task<bool> UpdateDistributorOrderStatus(string orderId, DistributorOrderStatus distributorOrderStatus, int updatedBy, string updatedByName, string logText)
        {
            await logDbContext.UpdateLog.InsertAsync(new UpdateLog
            {
                LogType = UpdateLogType.Order,
                LogSubType = "UpdateDisOrderStatus",
                LogText = logText,
                BusinessId = orderId,
                UpdatedBy = updatedBy,
                UpdatedByName = updatedByName,
                UpdatedTime = DateTime.Now,
            });

            return await dbContext.OrderHotelDistributor.UpdateAsync(e => new OrderHotelDistributor { 
                DistributorOrderStatus = distributorOrderStatus,
                UpdatedTime = DateTime.Now 
            }, 
            e => e.OrderId.Equals(orderId));
        }

        public async Task<string> InsertDistributorOrder(OrderHotelDistributor entity)
        {
            entity.OrderId = GenerateOrderId(entity.Distributor);
            await dbContext.OrderHotelDistributor.InsertAsync(entity);
            return entity.OrderId;
        }



        public async Task<OrderHotelSupplier> GetSupplierOrder(string supplier, string supplierOrderId)
        {
            return await dbContext.OrderHotelSupplier.GetSingleAsync(e => e.Supplier.Equals(supplier) && e.SupplierOrderId.Equals(supplierOrderId));
        }

        public async Task<List<OrderHotelSupplier>> GetSupplierOrderList(string orderId)
        {
            return await dbContext.OrderHotelSupplier.GetListAsync(e => e.OrderId.Equals(orderId));
        }

        public async Task<bool> UpdateSupplierOrderStatus(string orderId, string supplier, string supplierOrderId, OrderHotelStatus supplierOrderStatus, int updatedBy, string updatedByName, string logText)
        {
            if (await dbContext.OrderHotelSupplier.UpdateAsync(e => new OrderHotelSupplier { SupplierOrderStatus = supplierOrderStatus, UpdatedBy = updatedBy, UpdatedByName = updatedByName, UpdatedTime = DateTime.Now }, e => e.OrderId.Equals(orderId) && e.Supplier.Equals(supplier) && e.SupplierOrderId.Equals(supplierOrderId) && !e.SupplierOrderStatus.Equals(supplierOrderStatus)))
            {
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "UpdateSupOrderStatus",
                    LogText = logText,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });

                return true;
            }
            else
            {
                return false;
            }
        }

        public async Task<bool> UpdateSupplierOrder(string orderId, string supplier, string supplierOrderId, OrderHotelStatus supplierOrderStatus, Money settlementPrice, int updatedBy, string updatedByName, string logText)
        {
            if (await dbContext.OrderHotelSupplier.UpdateAsync(e => new OrderHotelSupplier { 
                SupplierOrderStatus = supplierOrderStatus, 
                SupplierCurrency = settlementPrice.Currency,
                SupplierPrice = settlementPrice.Amount,
                UpdatedBy = updatedBy, 
                UpdatedByName = updatedByName, 
                UpdatedTime = DateTime.Now 
            }, e => e.OrderId.Equals(orderId) 
                && e.Supplier.Equals(supplier) 
                && e.SupplierOrderId.Equals(supplierOrderId) 
                && !e.SupplierOrderStatus.Equals(supplierOrderStatus)))
            {
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "UpdateSupOrder",
                    LogText = logText,
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });

                return true;
            }
            else
            {
                return false;
            }
        }

        public async Task<bool> InsertSupplierOrder(OrderHotelSupplier entity)
        {
            return await dbContext.OrderHotelSupplier.InsertAsync(entity);
        }


        public async Task<bool> UpdateOrderStatus(string orderId, OrderHotelStatus orderStatus)
        {
            return await dbContext.OrderHotelDistributor.UpdateAsync(e => new OrderHotelDistributor { OrderStatus = orderStatus, UpdatedTime = DateTime.Now }, e => e.OrderId.Equals(orderId));
        }

        public async Task<bool> UpdateOrderStatus(string orderId, OrderHotelStatus orderStatus, int updatedBy, string updatedByName, string remark)
        {
            var orderInfo = await dbContext.OrderHotelDistributor.GetSingleAsync(e => e.OrderId.Equals(orderId));
            if (orderInfo == null)
                throw new NullReferenceException();

            if (orderInfo.OrderStatus == orderStatus)
                return false;

            if (await dbContext.OrderHotelDistributor.UpdateAsync(e => new OrderHotelDistributor { OrderStatus = orderStatus, UpdatedTime = DateTime.Now }, e => e.OrderId.Equals(orderId)))
            {
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "UpdateOrderStatus",
                    LogText = $"更新订单状态: [{EnumsHelper.GetDescription<OrderHotelStatus>(orderInfo.OrderStatus)}] -> [{EnumsHelper.GetDescription<OrderHotelStatus>(orderStatus)}], remarks: {remark}",
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });

                return true;
            }
            else
            {
                return false;
            }
        }

        public async Task<bool> UpdateHotelConfirmNo(string orderId, string hotelConfirmNo, int updatedBy, string updatedByName)
        {
            var orderInfo = await dbContext.OrderHotelDistributor.GetSingleAsync(e => e.OrderId.Equals(orderId));
            if (orderInfo == null)
                throw new NullReferenceException();

            if (orderInfo.HotelConfirmNo == hotelConfirmNo)
                return false;

            if (await dbContext.OrderHotelDistributor.UpdateAsync(e => new OrderHotelDistributor { HotelConfirmNo = hotelConfirmNo }, e => e.OrderId.Equals(orderId)))
            {
                await logDbContext.UpdateLog.InsertAsync(new UpdateLog
                {
                    LogType = UpdateLogType.Order,
                    LogSubType = "UpdateHotelConfirmNo",
                    LogText = $"更新酒店确认号: [{orderInfo.HotelConfirmNo}] -> [{hotelConfirmNo}]",
                    BusinessId = orderId,
                    UpdatedBy = updatedBy,
                    UpdatedByName = updatedByName,
                    UpdatedTime = DateTime.Now,
                });

                return true;
            }
            else
            {
                return false;
            }
        }

        public async Task<bool> UpdateHotelConfirmNo(string orderId, string hotelConfirmNo)
        {
            return await dbContext.OrderHotelDistributor.UpdateAsync(e => new OrderHotelDistributor { HotelConfirmNo = hotelConfirmNo }, e => e.OrderId.Equals(orderId));
        }



        /// <summary>
        /// 生成唯一OrderId
        /// </summary>
        /// <param name="distributor"></param>
        /// <returns></returns>
        private string GenerateOrderId(string distributor)
        {
            var prefix = "H";
            if (string.IsNullOrEmpty(distributor) || distributor.Equals("other"))
                prefix = "O";

            int maxSize = 9;
            var result = new StringBuilder(maxSize);

            var a = "1234567890";
            var chars = a.OrderBy(c => Guid.NewGuid().ToString()).ToArray();

            using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
            {
                var data = new byte[maxSize];
                crypto.GetNonZeroBytes(data);
                foreach (byte b in data)
                {
                    result.Append(chars[b % (chars.Length - 1)]);
                }
            }

            var orderId = string.Format("{0}{1}{2}", prefix, DateTime.Now.ToString("yyMMdd"), result.ToString());
            if (dbContext.OrderHotelDistributor.Count(e => e.OrderId.Equals(orderId)) > 0)
            {
                return GenerateOrderId(distributor);
            }
            else
            {
                return orderId;
            }
        }
    }
}
